#pragma once

#include <memory>
#include "Containers/Containers.h"
#include "ScriptCompiler/Description/IdentifierDescription.h"


#include "ScriptCompiler/Description/FieldDescription.h"
#include "ScriptCompiler/Description/PropertyDescription.h"
#include "ScriptCompiler/Description/FunctionDescription.h"
#include "ScriptCompiler/Description/TypeDescription.h"
#include "ScriptCompiler/Description/UsingDescription.h"
#include "ScriptCompiler/Description/ClassAlias.h"

class CTypeDescription;

class CNamespaceDescription : public IIdentifierDescription 
{

public:
    CNamespaceDescription()
    {
    }

    virtual EIdentifierType GetIdentifierType() const override
    {
        return EIdentifierType::Namespace;
    }

    //global namespace donot have parent
    std::weak_ptr<CNamespaceDescription> ParentScope;

    //TVector<std::shared_ptr<CNamespaceNode>> Nodes;

    //uint32_t IndexInParent = 0;

    TVector<CString> FullNameChain;

    bool bIsNative = false;

    bool IsGlobal() const
    {
        return FullNameChain.Num() == 0;
    }

    //TVector<std::shared_ptr<CUsingDescription>> Usings;    

    TVector<std::shared_ptr<CNamespaceDescription>> Namespaces;
 
    TVector<std::shared_ptr<CTypeDescription>> Classes;

    //the class that is used in this namespace , but not declared in this namespace
    TVector<std::shared_ptr<CClassAlias>> UsingClasses;

    //might be null for global namespace and native namespace and virtual namespace such as: in "namespace A.B.C {}",the A.B is virtual namespace
    //per declaration of namespace cooresponds to a namespace node,even in the same source file
    //if declared manay times, there will be many namespace nodes
    TVector<std::shared_ptr<CNamespaceNode>> ASTNodes;

    TVector<
        TTuple<
            std::weak_ptr<CScriptFileContext>
            , TVector<std::shared_ptr<CNamespaceDescription>>
        >
    > UsingNamespaces;
    
    virtual CString GetSearchName() const override
    {
        assert(FullNameChain.Num() > 0 && "global name space donot have search name" );
        return FullNameChain[FullNameChain.Num()-1];
    }

    //find namespace by name
    std::shared_ptr<CNamespaceDescription> FindNamespace(const CString & InSubNamespaceName)
    {
        for (auto& Namespace : Namespaces)
        {
            if (Namespace->GetSearchName() == InSubNamespaceName)
            {
                return Namespace;
            }
        }
        return nullptr;

    }

    std::shared_ptr<CNamespaceDescription> FindNamespace(const TVector<CString> & InNamespaceNameChain)
    {
        if (InNamespaceNameChain.size() == 0)
        {
            return nullptr;
        }

        auto Namespace = FindNamespace(InNamespaceNameChain[0]);
        if (Namespace == nullptr)
        {
            return nullptr;
        }

        if (InNamespaceNameChain.size() == 1)
        {
            return Namespace;
        }

        return Namespace->FindNamespace(InNamespaceNameChain.Slice(1, InNamespaceNameChain.Num()));

    }



    //find sub class by name
    std::shared_ptr<CTypeDescription> FindClass(const CString & InClassName)
    {
        for (auto& Class : Classes)
        {
            if (Class->Name == InClassName)
            {
                return Class;
            }
        }

        for (auto& UsingClass : UsingClasses)
        {
            if (UsingClass->AliasName == InClassName)
            {
                return UsingClass->Class;
            }
        }

        return nullptr;
    }

    //find a class in file context, the class is not nessasery sub class
    std::shared_ptr<CTypeDescription> FindClassInFile(
        std::shared_ptr<CScriptFileContext> InFile
        ,const CString & InClassName
    );

    std::shared_ptr<CNamespaceDescription> FindNamespaceInFile(
        std::shared_ptr<CScriptFileContext> InFile
        ,const CString & InNamespaceName
    );

    TOptional<uint32_t> FindClassIndex(const CString & InClassName)
    {
        for (uint32_t i = 0; i < Classes.size(); i++)
        {
            if (Classes[i]->GetSearchName() == InClassName)
            {
                return i;
            }
        }
        return TOptional<uint32_t>();
    }

    //find sub enum by name
    // std::shared_ptr<CTypeDescription> FindEnum(const CString & InEnumName)
    // {
    //     for (auto& Enum : Enums)
    //     {
    //         if (Enum->GetSearchName() == InEnumName)
    //         {
    //             return Enum;
    //         }
    //     }
    //     return nullptr;
    // }
    
};